;;; DR-11 Foonly microcode. ;;; Modified from Tymnet microcode March 1981 DAM ;A-MEM USAGE. ; 0: STANDARD. (microcode interrupt dispatch address) ; 1: STATE OF OUTPUT FFS FOR DEV6ST (SEE CFTNET PRINT) ; 2: INPUT HALFWORD COUNT ; 3: INPUT HALFWORD POINTER ; 4: OUTPUT HALFWORD COUNT ; 5: OUTPUT HALFWORD POINTER ; 6: INPUT BUFFER,,OUTPUT BUFFER ; 7: B0-2, STATE OF MAP BEFORE INT; B3-35, INP REG AT INT START ;The buffers in register 6 are used to cut the number of memory ;cycles in half. ;The counts are zero when the corresponding process is idle. ;MAIN MEMORY LOCATIONS: ; Refer to DR11.PROTOCOL ;DR11C FLAG NAMES. (FIELD VALUES). TNODIBN = 2 ;OUTPUT DONE ROTATE AMOUNT (INPUT) = REQ B. TNIRIBN = 3 ;INPUT READY ROTATE AMOUNT (INPUT) = REQ A. TNODOBN = 6 ;OUTPUT DONE ROTATE AMOUNT (OUTPUT) = CSR1. TNIROBN = 7 ;INPUT READY ROTATE AMOUNT (OUTPUT) = CSR0. TNODENB = 2 ;TNODIBN INTERRUPT ENABLE TNIRENB = 3 ;TNIRIBN INTERRUPT ENABLE ;TNODIBN and TNIRIBN are the edge versions, not the versions ;that come straight from the 11. In loop-back mode, pulsing ;TNODOBN causes TNODIBN to become 1, and pulsing TNIROBN causes ;TNIRIBN to become 1. TNODIBN is cleared by writing to subdevice ;TNODIFS, and TNIRIBN is cleared by writing to subdevice TNIRIFS. ;Microcode interrupt from device 6 comes here. This will be due ;to input ready or output done having been pulsed by the 11, ;and the corresponding enable bit set in the output register. ;We have to turn the map off in order to reference exec memory, ;because there is no hope at all for the getting the damned ;map-miss microcode to win due to the way the machine is designed. ;This means we can't address pages which are locked down but not ;in the same physical page as their virtual address. TNI: ;;Save map state in A-MEM[7].0-2 & turn map off. DEST[CLR-DEV-FROM-INTR] SPEC[IOB-IN] NORM PUSHJ[MAPOFF] $ D[IR] MASK[3] DEST[AR] MAPF[10] CYLEN[IOB-OUT] $ ;MAPF,CYLEN for MAPOFF D[AR] ROT[41] DEST[Q] SHORT $ D[CONST 6] DEST[DEV-ADR] SPEC[IOB-IN] NORM $ ;Back to device 6 ;;The map state is in Q. Read the DR-11 state, put that ;;in AR and the both of them in Q and A-MEM[7]. MAPF[TYM.RD] D[IOD] MASK[41] DEST[AR] CYLEN[IOB-IN] $ D[AR] ALU[DORQ] DEST[Q 7] DEST-A-MEM NORM $ ;;Is the output-done edge latch on? D[17] ROT[35. - TNODIBN] COND[-OBUS<0] JUMP[TNI1] C550 $ ;;Yes, run the output channel. D[14] ALU[D-1] DEST[AR] ;Decrement the halfword count COND[OBUS<0] JUMP[DROIDL] C600 $ ; and check for idle. D[AR] DEST[4] DEST-A-MEM NORM $ ;Write back decremented WC ;;Channel active, clear flag from 11 and see which half of word. D[15] MASK[1] COND[OBUS=0] JUMP[DRONWD] C550 $ D[16] MASK[16.] DEST[AR] PUSHJ[DRSEND] NORM $ ;Output second halfword D[15] ALU[D+1] DEST[AR] NORM $ ;Increment pointer TNI0: D[AR] DEST[5] DEST-A-MEM JUMP[TNI1] NORM $ DROSTP: ;; Turn off output interrupt enable (bit 33) D[CONST 1] ROT[TNODENB] ALU[NOTD] DEST[Q] PUSHJ[DEV6CL] NORM $ TNI1: ;;Is the input-ready edge latch on? D[17] ROT[35. - TNIRIBN] COND[-OBUS<0] JUMP[TNI9] C550 $ ;;Yes, run the input channel. Copy input data into AR. D[17] ROT[34] MASK[20] DEST[AR] NORM $ ;;Check for packet in progress or start of new packet. D[12] ALU[D] COND[OBUS=0] JUMP[DRINPK] C550 $ ;;Check for need to store a word D[13] ROT[43] DEST[MA] COND[OBUS<0] JUMP[DRINWD] C550 $ ;;Just buffer this halfword D[16] MASK[18.] DEST[Q] NORM $ D[AR] ROT[20.] ALU[DORQ] DEST[6] DEST-A-MEM PUSHJ[DRTAKE] NORM $ ;;Increment pointer, decrement counter D[13] ALU[D+1] DEST[AR] NORM $ TNI2: D[AR] DEST[3] DEST-A-MEM NORM $ D[12] ALU[D-1] DEST[AR] NORM $ D[AR] DEST[2] DEST-A-MEM COND[-OBUS=0] JUMP[TNI9] C600 $ ;;That was the end of a packet, deliver to 10 D[13] ROT[43] DEST[MA] COND[-OBUS<0] JUMP[TNI3] C550 $ ;;Oops, there is one halfword still to be stored D[16] SPEC[LEFT] DEST[MEMSTO] NORM $ TNI3: ;;Read & clear input packet in progress and add to input done list D[CONST 4] DEST[Q] PUSHJ[TNRCW] CYLEN[MEMSTO] $ D[MEM] MASK[18.] DEST[Q] SHORT $ ALU[0] DEST[MEMSTO] NORM $ D[CONST 20] ROT[6] ALU[D] DEST[MA] PUSHJ[DRPUSH] CYLEN[MEMSTO] $ TNI9: ;;Done: restore state of map and exit back to MAIN D[17] ROT[3] MASK[3] DEST[IOD] CYLEN[MEMSTO] $ ALU[0] DEST[DEV-ADR] SPEC[IOB-OUT] JUMP[MPOFF1] NORM $ DRINWD: ;;Input channel has two halfwords to store D[AR] ROT[4] DEST[Q] SHORT $ D[16] SPEC[LEFT] ALU[DORQ] DEST[MEMSTO] PUSHJ[DRTAKE] NORM $ D[13] ALU[D+1] DEST[AR] JUMP[TNI2] NORM $ DRINPK: ;;Do nothing if interrupt not enabled D[11] ROT[35. - TNIRENB] COND[-OBUS<0] JUMP[TNI9] C550 $ ;;AR has length of next packet 11 is trying to send D[AR] COND[OBUS=0] JUMP[DRINP9] C550 $;Ignore zero length ;;Check input ready list, shut down if empty D[CONST 2] DEST[Q] PUSHJ[TNRCW] NORM $ D[MEM] MASK[18.] DEST[IR-ADR] ;Save last pkt in IR RT COND[OBUS=0] JUMP[DRISTP] C550 $ ;;Get header of first packet on list D[MEM] ROT[18.] MASK[18.] DEST[MA] PUSHJ[CFIXM0] NORM $ ;;Compare length field in header against length from 11, ;;ignore it if too large (this aids resynchronization after booting) D[MEM] ROT[18.] MASK[18.] DEST[Q] SHORT $ D[AR] ALU[Q-D] COND[OBUS<0] JUMP[DRINP9] C600 $ ;;Q gets new list header: cdr of that packet,,old last D[IR] MASK[18.] DEST[Q] SHORT $ D[MEM] ROT[18.] SPEC[LEFT] ALU[DORQ] DEST[Q] SHORT $ ;;Smash packet's header with incoming length and 0 cdr D[AR] ROT[18.] DEST[MEMSTO] NORM $ ;;Store packet length in A memory D[AR] DEST[2] DEST-A-MEM NORM $ ;Save word count ;;Set up the input halfword pointer to point to it. D[MA] MASK[18.] ALU[D+1] DEST[AR] CYLEN[MEMSTO] $ D[AR] ROT[1] DEST[3] DEST-A-MEM NORM $ ;;Cdr the ready list D[MASK 18.] ROT[18.] ALU[D&Q] DEST[AR] COND[OBUS=0] JUMP[DRINP1] C550 $ ALU[Q] DEST[AR] SHORT $ DRINP1: D[CONST 2] DEST[Q] PUSHJ[TNRCW] NORM $ D[AR] DEST[MEMSTO] NORM $ ;; Store the packet into packet in progress D[CONST 4] DEST[Q] PUSHJ[TNRCW] CYLEN[MEMSTO] $ D[13] ROT[43] ALU[D-1] DEST[MEMSTO] NORM $ DRINP9: ;;Take input from 11 and return PUSHJ[DRTAKE] CYLEN[MEMSTO] $ JUMP[TNI9] NORM $ DRISTP: ;;No place to put this packet, stop input channel ;; Turn off input interrupt enable (bit 32) D[CONST 1] ROT[TNIRENB] ALU[NOTD] DEST[Q] PUSHJ[DEV6CL] NORM $ ;;Clear packet word count, the -11 is still sending it ALU[0] DEST[2] DEST-A-MEM JUMP[TNI9] NORM $ ;Output channel needs to fetch another word DRONWD: ;;Start read of word, also finish clearing output-done flag D[15] ROT[43] MASK[18.] DEST[MA] PUSHJ[CFIXM0] NORM $ D[16] SPEC[LEFT] DEST[Q] NORM $ ;Copy input buffer into Q D[MEM] ROT[32.] MASK[16.] ALU[DORQ] ;Merge second hwd into buf DEST[6] DEST-A-MEM NORM $ D[MEM] ROT[16.] MASK[16.] DEST[AR] ;Transmit first halfword PUSHJ[DRSEND] NORM $ D[15] ALU[D+1] DEST[AR] JUMP[TNI0] NORM $ ;Increment pointer ;Here when DR11 says output done and we have no current output packet DROIDL: ;;Check interrupt enable. If 0, don't do anything. D[11] ROT[35. - TNODENB] COND[-OBUS<0] JUMP[TNI1] C550 $ ;;Clean up packet in progress if any. D[CONST 5] DEST[Q] PUSHJ[TNRCW] NORM $ D[MEM] MASK[18.] DEST[Q] COND[OBUS=0] JUMP[DROI1] C550 $ ALU[0] DEST[MEMSTO] NORM $ ;Store back zero D[CONST 20] ROT[6] ALU[D+1] DEST[MA] ;Add packet in Q to list PUSHJ[DRPUSH] CYLEN[MEMSTO] $ DROI1: ;;Check output ready list. D[CONST 3] DEST[Q] PUSHJ[TNRCW] CYLEN[MEMSTO] $ D[MEM] COND[OBUS=0] JUMP[DROSTP] C550 $ ;Shut down if empty ;;Get header of first packet on list D[MEM] ROT[18.] MASK[18.] DEST[Q MA] PUSHJ[CFIXM0] NORM $ ;;Set up the output halfword pointer to point to it. ALU[Q+1] DEST[AR] SHORT $ D[AR] ROT[1] DEST[5] DEST-A-MEM NORM $ ;;Save the halfword count, and send it to the 11. D[MEM] ROT[18.] MASK[16.] DEST[AR 4] DEST-A-MEM PUSHJ[DRSEND] NORM $ ;;Save the cdr: second packet on list. D[MEM] ROT[18.] SPEC[LEFT] DEST[AR] SHORT $ ;;Clobber the cdr to zero. D[MEM] SPEC[LEFT] DEST[MEMSTO] NORM $ ;;Cdr the ready list D[CONST 3] DEST[Q] PUSHJ[TNRCW] CYLEN[MEMSTO] $ D[AR] DEST[Q] COND[OBUS=0] JUMP[DROI2] C550 $ D[MEM] MASK[18.] ALU[DORQ] DEST[MEMSTO] NORM JUMP[DROI3] $ DROI2: ALU[0] DEST[MEMSTO] NORM $ ;empty DROI3: ;; Store the packet into packet in progress D[CONST 5] DEST[Q] PUSHJ[TNRCW] CYLEN[MEMSTO] $ D[15] ROT[43] ALU[D-1] DEST[MEMSTO] JUMP[TNI1] $ ;;; Subroutines ;AR<20:35> contains 16 bits to go out the DR-11, rest of AR is zero ;This clears the output-done flag then sends new data then pulses the ;input-ready line as seen by the 11. DRSEND: D[AR] ROT[8] DEST[AR] SPEC[IOB-OUT] NORM $ ;Align as hardware wants ;; Output the data and drop TNIROBN (input ready seen by 11) D[MASK 8] DEST[Q] MAPF[TNODIFS] CYLEN[IOB-OUT] $ D[CONST 1] ROT[TNIROBN] ALU[-D&Q] DEST[Q] PUSHJ[DEV6ST1] NORM $ ;; Raise to-11 input ready again and return D[CONST 1] ROT[TNIROBN] DEST[AR] JUMP[DEV6ST2] NORM $ ;;;Take input from 11, smashing Q and AR ;;;This both clears the ready-for-input interrupt and pulses ;;;the CSR1 line, telling the 11 to send more input. DRTAKE: ALU[0] DEST[AR] SPEC[IOB-OUT] CYLEN[MEMSTO] $ D[MASK 43] ROT[TNODOBN + 1] DEST[Q] PUSHJ[DEV6ST1] MAPF[TNIRIFS] CYLEN[IOB-OUT] $ D[CONST 1] ROT[TNODOBN] DEST[AR] NORM JUMP[DEV6ST2] $ ;Read memory location 2000+Q TNRCW: D[CONST 20] ROT[6] ALU[DORQ] DEST[MA] NORM $ CFIXM0: FIXM0 POPJ $ ;Put packet in Q onto list whose header address is in MA ;Its cdr must already be zero. ;Location returned to must have CYLEN[MEMSTO] DRPUSH: FIXM0 $ ;; Is list empty? D[MEM] DEST[AR] COND[OBUS=0] JUMP[DRPSH1] C550 $ ;; Preserve first, change last D[AR] SPEC[LEFT] ALU[DORQ] DEST[MEMSTO] NORM $ ;; Rplacd old last to new last D[AR] MASK[18.] DEST[MA] PUSHJ[CFIXM0] CYLEN[MEMSTO] $ D[MEM] SPEC[LEFT] ALU[DORQ] DEST[MEMSTO] POPJ $ ;List was empty, store back Q,,Q DRPSH1: ALU[Q] DEST[AR] SHORT $ D[AR] ROT[18.] ALU[DORQ] DEST[MEMSTO] POPJ $ ;;; IOT Instructions ;;; It's too much of a pain to make these priveleged, since you have to ;;; check for USR IOT mode manually. And the TYMNET ones aren't priveleged. ;;; We aren't fascist anyway. TYMAREA: ;SAVE LOCATION .OPCODE[744] ;RESET TYMNET D[IR] ROT[13.] MASK[2] COND[OBUS=0] JUMP[DR11R] C550 $ D[IR] ROT[13.] MASK[1] COND[-OBUS=0] LBJUMP[DR11WO] C550 $ .OPCODE[745] ;DIRECT OUTPUT D[CONST 6] DEST[DEV-ADR] JUMP[DR11O] NORM $ NOP $ .OPCODE[746] ;DIRECT INPUT D[CONST 6] DEST[DEV-ADR] NORM JUMP[DR11I] $ NOP $ .OPCODE[747] ;DEBUG (DUMP A-MEMORY) ((FORMERLY F5DIAG)) D[CONST 6] DEST[DEV-ADR] NORM JUMP[DR11DB] $ NOP $ .USE[TYMAREA] ;RETURN TO MAIN CODE ;;; 744 0,E (DR11R) Reset the microcode, and send E to the control register: ;;; 30-31 CSR0, CSR1 to 11 (input ready, output done) ;;; 32-33 not connected ;;; 34-35 interrupt enables (input, output) DR11R: D[MA] ROT[2] DEST[AR] SHORT $ ;Align control register D[MASK 6] ROT[2] ALU[NOTD] DEST[Q] PUSHJ[DEV6ST] NORM $ ALU[0] DEST[2] DEST-A-MEM NORM $ ;Reset both halfword counters ALU[0] DEST[4] DEST-A-MEM NORM $ ALU[0] DEST[3] DEST-A-MEM NORM $ ;Clear other locations only ALU[0] DEST[5] DEST-A-MEM NORM $ ; to make debugging easier ALU[0] DEST[6] DEST-A-MEM NORM $ ; .. SPEC[IOB-OUT] NORM $ MAPF[TNODIFS] SPEC[IOB-OUT] CYLEN[IOB-OUT] $ ;Reset output done flag MAPF[TNIRIFS] CYLEN[IOB-OUT] JUMP[DR11R1] $ ;Reset input ready flag .PAIR ;;; 744 2,0 (DR11WO) Wake up the output microcode DR11WO: D[CONST 1] ROT[TNODENB] ALU[NOTD] DEST[Q] JUMP[DR11W] NORM $ ;;; 744 1,0 (DR11WI) Wake up the input microcode D[CONST 1] ROT[TNIRENB] ALU[NOTD] DEST[Q] SHORT $ DR11W: ALU[NOTQ] DEST[AR] PUSHJ[DEV6ST] NORM $ DR11R1: ALU[0] DEST[DEV-ADR] JUMP[MAIN] NORM $ ;;; 745 0,E (DR11O) Output E low 16 bits to pdp11 DR11O: D[MA] MASK[16.] DEST[AR] PUSHJ[DRSEND] NORM $ ;Transmit data ALU[0] DEST[DEV-ADR] JUMP[MAIN] NORM $ ;;; 746 0,E (DR11I) Input from pdp11 to C(E) DR11I: D[CONST 25.] LLOAD NORM $ NORM LOOP[.] $ ;GIVE DMA DEVICES SOME MEMORY TIME. (???????) SPEC[IOB-IN] NORM $ MAPF[TYM.RD] D[IOD] ROT[27. + 1] DEST[AR MEMSTO] PUSHJ[STONXT] C800 $ ;Align input word with 16 data bits at RIGHT end. D[AR] ROT[4] C550 COND[-OBUS<0] JUMP[. + 2] $ ;Don't clear "INPUT RDY" flag unless it's on. PUSHJ[DRTAKE] NORM $ ;it's on ALU[0] DEST[DEV-ADR] JUMP[MAIN] NORM $ ;;; 747 0,E (DR11DB) Dump device A-memory into E..E+7 DR11DB: D[10] DEST[MEMSTO] PUSHJ[STONXT] NORM $ D[11] DEST[MEMSTO] PUSHJ[STONXT] NORM $ D[12] DEST[MEMSTO] PUSHJ[STONXT] NORM $ D[13] DEST[MEMSTO] PUSHJ[STONXT] NORM $ D[14] DEST[MEMSTO] PUSHJ[STONXT] NORM $ D[15] DEST[MEMSTO] PUSHJ[STONXT] NORM $ D[16] DEST[MEMSTO] PUSHJ[STONXT] NORM $ D[17] DEST[MEMSTO] PUSHJ[STONXT] NORM $ ALU[0] DEST[DEV-ADR] JUMP[MAIN] NORM $ STONXT: MAPF[NORM-WRT] COND[-MA-AC] JUMP[STONX1] CYLEN[MEMSTO] $ ACSEL[MA] D[MEM] DEST[AC] NORM $ STONX1: D[MA] ALU[D+1] DEST[MA] POPJ NORM $